<?php
/**
 * @desc db class file
 * @author nana
 * @date 2011
 * @need transaction and databaseresult class, DIR_CORE.'db'.DS.'database.php';
 *
 */

if (class_exists('MySQLi'))
{
	class NICED_MySQLi extends MySQLi implements NICED_Database
	{
		private $dsn;
        private $dbSetting;
		private $sqlStatement;
		private $connected = false;
		private $queries = array();
		private $queryCount = 0;
		private $lastQueryTime;

		private $debug = false;
		private $maxQueryLogCount = 5;
		private $slowQueryThreshold = 0.1;
		private $logSlow       = true;
        private $retry         = 3;

		public function __construct($dsn, $db_setting = NULL) 
		{
			$this->dsn = $dsn;

            if (!$db_setting)
            {
                $db_setting = NICED_DBFactory::getDsnSetting($this->dsn);
            }

            if (!$db_setting)
            {
                throw new NICED_DatabaseException('no db setting for dsn:'.$this->dsn);
            }

            $this->dbSetting = $db_setting;
            $this->initConfig();
		}

        private function initConfig()
        {
            $this->debug              = LC::get("database.mysqli.debug") && SC::get("board_config.debug");
            $this->maxQueryLogCount   = LC::get("database.mysqli.max_query_log_count");
            $this->slowQueryThreshold = LC::get("database.mysqli.slow_query_threshold");
            $this->logSlow            = LC::get("database.mysqli.log_slow");
            $this->retry              = LC::get("database.mysqli.retry");
        }

		public function query($sql) 
		{
			$this->sqlStatement = $sql;
			$start = microtime(true);

			if (!$this->isConnected()) 
			{
				$this->getConnect();
				if (!$this->isConnected())
				{
					throw new NICED_DatabaseException("connect to db fail");
				}
			}
			
			$rst = parent::query($sql);
			$end = microtime(true);
			$this->lastQueryTime = time();

			if (!$rst) 
			{
				throw new NICED_DatabaseException("db execute query fail, error from db server :".$this->error.",sql is ".$sql);
			}

			$result = new NICED_MySQLiResult($this, $rst, $sql);

		    $this->addQuery($result, $start, $end);

			return $result;
		}

		private function getConnect() 
		{
            $db_setting = &$this->dbSetting;


			$type   = isset($db_setting['type'])    ? $db_setting['type']    : '';
			$ip     = isset($db_setting['ip'])      ? $db_setting['ip']      : '';
			$port   = isset($db_setting['port'])    ? $db_setting['port']    : '';
			$dbname = isset($db_setting['dbname'])  ? $db_setting['dbname']  : '';
			$user   = isset($db_setting['user'])    ? $db_setting['user']    : '';
			$pass   = isset($db_setting['pass'])    ? $db_setting['pass']    : ''; 
			$charset= isset($db_setting['charset']) ? $db_setting['charset'] : ''; 
			
			try
			{
                for ($i = 0; $i <= $this->retry; $i++)
                {
				    parent::__construct($ip, $user, $pass, $dbname, $port);
                    if (!$this->connect_error)
                    {
				        $this->connected = true;
                        if ($i > 0)
                        {
                           NICED_ALARM("mysqli_connect_succ_after_retry");
                        }
                        break; 
                    }
                    else
                    {
                        NICED_ALARM("mysqli_connect_error");
                    }
                }

                //2003 is connect fail errno
				if ($this->connect_error && 2003 == $this->connect_errno)
				{
                    NICED_ALARM("mysql_server_failover");

                    $ip     = isset($db_setting["failover_server"]['ip'])      ? $db_setting["failover_server"]['ip']      : '';
                    $port   = isset($db_setting["failover_server"]['port'])    ? $db_setting["failover_server"]['port']    : '';
                    $user   = isset($db_setting["failover_server"]['user'])    ? $db_setting["failover_server"]['user']    : '';
                    $pass   = isset($db_setting["failover_server"]['pass'])    ? $db_setting["failover_server"]['pass']    : ''; 

                    for ($i = 0; $i < $this->retry; $i++)
                    {
                        parent::__construct($ip, $user, $pass, $dbname, $port);
                        if (!$this->connect_error)
                        {
				            $this->connected = true;
                            if ($i > 0)
                            {
                                NICED_ALARM("mysqli_connect_succ_after_retry");
                            }
                            break; 
                        }
                        else
                        {
                            NICED_ALARM("mysqli_connect_error");
					        throw new NICED_DatabaseException("db connection error！".$this->connect_error.", error no".$this->connect_errno);
                        }
                    }
				}
                else if ($this->connect_error)
                {
                    NICED_ALARM("mysqli_connect_fail");
					throw new NICED_DatabaseException("db connection error！".$this->connect_error.", error no".$this->connect_errno);
                }

                if (!$this->connected || $this->error)
                {
                    throw new NICED_DatabaseException("not connect to mysql server.error info :".$this->error.", error no: ".$this->errno);
                }

				if ($charset)
				{
					$this->set_charset($charset);
				}
			} 
			catch (Exception $e) 
			{
				throw new NICED_DatabaseException($e->getMessage());
			}

			return $this;
		}

		private function isConnected() 
		{
			return $this->connected;
		}

		private function addQuery($rs, $start = null, $end = null)
		{
			if(!$this->debug) 
			{
				//return;
			}

			$this->queryCounter++;

			if ($this->logSlow && (($interval = $end - $start) > $this->slowQueryThreshold))
			{
				NICED_LOG(LC::get("database.pdo.log_level"), NICED.' slow query log from mysqli: '.$this->sqlStatement.' exec time is '.$interval);
			}

			if($this->queryCount > $this->maxQueryLogCount) 
			{
				$this->debug = false;
			}

            $this->queries[] = array(
                                    'type'=>'query',
                                    'count'=>$this->queryCounter, 
                                    'start'=>$start, 
                                    'end'=>$end, 
                                    'rs'=>$rs, 
                                    //'backtrace'=> debug_backtrace(),
                                );

            //alarm
            NICED_ALARM("mysqli_slow_query");
		}

		public function createTransaction()
		{
			return new NICED_MysqlTransaction($this);
		}

        public function logQueries()
        {
            $sql_log_str = "";
            foreach ($this->queries as $v)
            {
                $rs = $v["rs"];
                $sql = $rs->getSQL();
                $sql_log_str .= $sql;//.", db setting is : ".var_export($rs->getDB()->getDBSetting(), true).";";
            }

            NICED_LOG(LC::get("database.pdo.log_level"), NICED." all sql is ".$sql_log_str.", driver is ".__CLASS__);
        }

		public function close()
		{
	        $this->logQueries();	
		}

        public function getDBSetting()
        {
            return $this->dbSetting;
        }

	}//end class
}
